package app

import (
	"bytes"
	"crypto/md5"
	"errors"
	"fmt"
	"gitee.com/Rekeeper/orange/cfg"
	"io"
	"os"
	"path"
	"time"
)

type UploadFile struct {
	FileName  string
	Extension string
	FileSize  int64
	FileByte  []byte
	SavePath  string
}

type ErrNoFile struct {
	content string
}

func (e ErrNoFile) Error() string {
	return e.content
}

// AppUpload 匹配配置文件中的大小限制和扩展名限制
func (c *Context) AppUpload(formName string) (savePath string, err error) {
	maxSize := cfg.GetInt64("app.upload.maxSize", cfg.ConfigDef.GetInt64("app.upload.maxSize"))
	exts := cfg.GetSliceString("app.upload.ext", cfg.ConfigDef.GetSliceString("app.upload.ext"))
	storageDir := cfg.GetString("app.upload.storage", cfg.ConfigDef.GetString("app.upload.storage"))

	if storageDir == "" {
		return savePath, errors.New("config app.upload.storage is null")
	}
	storageDirLen := len(storageDir)
	if storageDir[storageDirLen-1:storageDirLen] == "/" {
		storageDir = storageDir[storageDirLen : storageDirLen-1]
	}

	nowTime := time.Now().Format("2006-01-02 15:04:05")
	year := nowTime[:4]
	month := nowTime[5:7]
	subDir := year + "/" + month
	storageDir = storageDir + "/" + subDir

	verifyFunc := func(file *UploadFile) error {
		if file.FileSize > maxSize {
			return errors.New("file size over the limit")
		}
		extAllow := false
		for _, e := range exts {
			if "."+e == file.Extension {
				extAllow = true
				break
			}
		}
		if extAllow == false {
			return errors.New("file extension deny upload")
		}

		fileNameByte := []byte(file.FileName + nowTime)
		fileName := fmt.Sprintf("%x", md5.Sum(fileNameByte))
		file.FileName = fileName + file.Extension
		return nil
	}

	saveFileName, err := c.UploadFromFile(formName, storageDir, verifyFunc)

	return subDir + "/" + saveFileName, err
}

// AppUploadToData 将上传后的文件返回 []byte 数据
func (c *Context) AppUploadToData(formName string) (*UploadFile, error) {
	maxSize := cfg.GetInt64("app.upload.maxSize", cfg.ConfigDef.GetInt64("app.upload.maxSize"))
	exts := cfg.GetSliceString("app.upload.ext", cfg.ConfigDef.GetSliceString("app.upload.ext"))

	nowTime := time.Now().Format("2006-01-02 15:04:05")
	year := nowTime[:4]
	month := nowTime[5:7]
	subDir := year + "/" + month

	verifyFunc := func(file *UploadFile) error {
		if file.FileSize > maxSize {
			return errors.New("file size over the limit")
		}
		extAllow := false
		for _, e := range exts {
			if "."+e == file.Extension {
				extAllow = true
				break
			}
		}
		if extAllow == false {
			return errors.New("file extension deny upload")
		}

		fileNameByte := []byte(file.FileName + nowTime)
		fileName := fmt.Sprintf("%x", md5.Sum(fileNameByte))
		file.FileName = fileName + file.Extension
		return nil
	}

	fileInfo, err := c.UploadFromFileToData(formName, subDir, verifyFunc)

	return fileInfo, err
}

// UploadFromFile
func (c *Context) UploadFromFile(formName string, saveDir string, verifyFunc func(file *UploadFile) error) (fileName string, err error) {
	file, handler, err := c.request.FormFile(formName)
	if err != nil {
		return fileName, ErrNoFile{err.Error()}
	}
	defer file.Close()
	fileInfo := &UploadFile{
		FileName:  handler.Filename,
		Extension: path.Ext(handler.Filename),
		FileSize:  handler.Size,
	}

	err = verifyFunc(fileInfo)
	if err != nil {
		return fileName, err
	}

	err = os.MkdirAll(saveDir, 0755)
	if err != nil {
		return fileName, err
	}

	f, err := os.OpenFile(saveDir+"/"+fileInfo.FileName, os.O_CREATE|os.O_WRONLY|os.O_CREATE, 0755)
	if err != nil {
		return fileName, err
	}
	defer f.Close()

	_, err = io.Copy(f, file)
	if err != nil {
		return fileName, err
	}

	return fileInfo.FileName, nil
}

// UploadFromFileToData
func (c *Context) UploadFromFileToData(formName string, saveDir string, verifyFunc func(file *UploadFile) error) (fileInfo *UploadFile, err error) {
	file, handler, err := c.request.FormFile(formName)
	if err != nil {
		return fileInfo, ErrNoFile{err.Error()}
	}
	defer file.Close()
	fileInfo = &UploadFile{
		FileName:  handler.Filename,
		Extension: path.Ext(handler.Filename),
		FileSize:  handler.Size,
		SavePath:  saveDir + "/" + handler.Filename,
	}

	err = verifyFunc(fileInfo)
	if err != nil {
		return fileInfo, err
	}

	buf := bytes.NewBuffer(make([]byte, 0))
	buf.ReadFrom(file)
	fileInfo.FileByte = buf.Bytes()

	return fileInfo, nil
}
